/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.util;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * This is a Factory class which creates matrices that are stored either in row
 * major or column major formats.
 * 
 * @author Shrideep Pallickara
 * 
 */
public class MatrixFactory {

    private static final MatrixFactory instance = new MatrixFactory();


    private MatrixFactory() {
    }


    /**
     * Retrieve the singleton instance of the Matrix Factory.
     * 
     * @return the matrix factory instance.
     */
    public static MatrixFactory getInstance() {
        return instance;
    }


    /**
     * Retrieve/create a matrix based on the specified arguments.
     * 
     * @param filename
     *            The file associated with the matrix.
     * @param numOfRows
     *            The number of rows within the matrix
     * @param numOfColumns
     *            The number of columns for this matrix
     * @param entriesType
     *            The DATA type for the entries. See the <code>Matrix</code>
     *            class in this package for more details.
     * @param rowMajorFormat
     *            Indicates whether the data is stored (needs to be stored) in
     *            ROW MAJOR format.
     * @return The initialized <code>Matrix</code> instance.
     * @throws IOException
     *             If there are problems reading/writing from the file system.
     */
    public Matrix getMatrix(String filename, int numOfRows, int numOfColumns,
                            int entriesType, boolean rowMajorFormat)
        throws IOException {
        Matrix matrix = null;
        if (rowMajorFormat) {
            matrix =
                new Matrix(filename, numOfRows, numOfColumns, rowMajorFormat,
                           entriesType);
        } else {
            matrix =
                new MatrixColumnMajor(filename, numOfRows, numOfColumns,
                                      rowMajorFormat, entriesType);
        }

        return matrix;
    }


    /**
     * Retrieve the <code>Matrix</code> associated with the matrix that was
     * stored in a file.
     * 
     * @param filename
     *            The file where the matrix was stored
     * @return The <code>Matrix</code> instance
     * @throws IOException
     *             If there are problems reading/writing from the file system.
     */
    public Matrix getMatrix(String filename) throws IOException {
        Matrix matrix = null;
        boolean rowMajorFormat = isRowMajorFormat(filename);
        if (rowMajorFormat) {
            matrix = new Matrix(filename);
        } else {
            matrix = new MatrixColumnMajor(filename);
        }

        return matrix;
    }


    /*
     * Checks to see if the matrix has been stored in ROW MAJOR format or COLUMN
     * MAJOR format.
     */
    private boolean isRowMajorFormat(String filename) throws IOException {
        int META_DATA_LENGTH = 16;
        ByteBuffer metadataBuffer = ByteBuffer.allocate(META_DATA_LENGTH);

        RandomAccessFile raf = new RandomAccessFile(filename, "rw");
        FileChannel fileChannel = raf.getChannel();
        fileChannel.position(0);
        fileChannel.read(metadataBuffer);

        metadataBuffer.flip();

        int numOfRows = metadataBuffer.getInt();
        int numOfColumns = metadataBuffer.getInt();

        int rowMajorIndicator = metadataBuffer.getInt();
        boolean rowMajorFormat = true;
        if (rowMajorIndicator == 1) {
            rowMajorFormat = false;
        } else {
            rowMajorFormat = true;
        }

        int entriesType = metadataBuffer.getInt();
        return rowMajorFormat;
    }


}
